package cn.tedu.sims.service.impl;

import cn.tedu.sims.ex.ServiceException;
import cn.tedu.sims.mapper.LocationMapper;
import cn.tedu.sims.mapper.RoleMapper;
import cn.tedu.sims.mapper.UserMapper;
import cn.tedu.sims.mapper.UserRoleMapper;
import cn.tedu.sims.pojo.dto.UserLoginByEmailDTO;
import cn.tedu.sims.pojo.dto.UserLoginByPhoneDTO;
import cn.tedu.sims.pojo.dto.UserLoginDTO;
import cn.tedu.sims.pojo.dto.UserRegDTO;
import cn.tedu.sims.pojo.entity.Location;
import cn.tedu.sims.pojo.entity.User;
import cn.tedu.sims.pojo.entity.UserRole;
import cn.tedu.sims.pojo.vo.LocationListItemVO;
import cn.tedu.sims.pojo.vo.UserLoginByUserNameAndRoleVO;
import cn.tedu.sims.pojo.vo.UserLoginInfoVO;
import cn.tedu.sims.security.UserDetails;
import cn.tedu.sims.service.UserService;
import cn.tedu.sims.util.EmailUtil;
import cn.tedu.sims.util.YzmUtil;
import cn.tedu.sims.web.ServiceCode;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserMapper userMapper;
    @Autowired
    UserRoleMapper userRoleMapper;
    @Autowired
    RoleMapper roleMapper;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private LocationMapper locationMapper;

    @Value("${sims.jwt.secret-key}")
    private String secretKey;
    @Value("${sims.jwt.duration-in-minute}")
    private long durationInMinute;
    @Autowired
    private EmailUtil emailUtil;
    @Autowired
    RedisTemplate<String, String> redisTemplate;

    @Override//注册
    public void insert(UserRegDTO userRegDTO) {
            int count = userMapper.countByUserName(userRegDTO.getUsername());
            if (count > 0) {
                throw new ServiceException(ServiceCode.ERR_CONFLICT, "注册失败,用户名重复");
            }


            UserLoginInfoVO queryResult = userMapper.getByPhone(userRegDTO.getPhone());
            if (queryResult !=null) {
                throw new ServiceException(ServiceCode.ERR_CONFLICT, "注册失败,该手机号码已注册!");
            }


            UserLoginInfoVO userLoginInfoVO = userMapper.getByEmail(userRegDTO.getEmail());
            if (userLoginInfoVO !=null) {
                throw new ServiceException(ServiceCode.ERR_CONFLICT, "注册失败,该邮箱地址已注册!");
            }

            //根据地区名查询地区id
            int locationId = locationMapper.countById(userRegDTO.getLocationId());
            if (locationId ==0){
                throw new ServiceException(ServiceCode.ERR_INSERT,"注册失败,该地区未开发!");
            }


        User user=new User();
        BeanUtils.copyProperties(userRegDTO,user);
        //密码加密
        BCryptPasswordEncoder encoder=new BCryptPasswordEncoder();
        user.setPassword(encoder.encode(userRegDTO.getPassword()));

        //内部单位为0 用户
        user.setInternalUnitId(0L);
        //设置默认状态为0
        user.setState(0);
        //注册时地区的关联ID
         user.setLocationId(userRegDTO.getLocationId());
        //注册时间
        user.setCreated(LocalDateTime.now());
        //数据创建时间
        user.setGmtCreate(LocalDateTime.now());
        int sum= userMapper.insert(user);
        if (sum!=1){
            throw new ServiceException(ServiceCode.ERR_INSERT,"注册失败,服务器繁忙");
        }

        //添加默认角色
        UserRole userRole=new UserRole();
        //用户ID
        userRole.setUserId(user.getId());
        //默认角色ID为普通用户
        userRole.setRoleId(2L);
        //数据创建时间
        userRole.setGmtCreate(LocalDateTime.now());
        int sum1= userRoleMapper.insert(userRole);
        if (sum1!=1){
            throw new ServiceException(ServiceCode.ERR_INSERT,"填写默认权限失败,出错了!");
        }

    }

    @Override//登陆
    public String login(UserLoginDTO userLoginDTO) {

        log.debug("开始处理【用户登录】的业务，参数：{}", userLoginDTO);
        // 执行认证
        Authentication authentication
                = new UsernamePasswordAuthenticationToken(
                userLoginDTO.getUsername(), userLoginDTO.getPassword());
        Authentication authenticateResult
                = authenticationManager.authenticate(authentication);
        log.debug("认证通过，认证管理器返回：{}", authenticateResult);

        // 从认证结果中获取所需的数据，将用于生成JWT
        Object principal = authenticateResult.getPrincipal();
        log.debug("认证结果中的当事人类型：{}", principal.getClass().getName());
        UserDetails userDetails = (UserDetails) principal;
        String username = userDetails.getUsername();
        Long id = userDetails.getId();
        Collection<GrantedAuthority> authorities = userDetails.getAuthorities();
        String authoritiesJsonString = JSON.toJSONString(authorities);
        // 生成JWT数据时，需要填充装到JWT中的数据
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", id);
        claims.put("username", username);
        claims.put("authoritiesJsonString", authoritiesJsonString);
        log.debug("向JWT中存入id：{}", id);
        log.debug("向JWT中存入username：{}", username);
        log.debug("向JWT中存入authoritiesJsonString：{}", authoritiesJsonString);
        // 以下是生成JWT的固定代码
        Date date = new Date(System.currentTimeMillis() + durationInMinute * 60 * 1000L);
        String jwt = Jwts.builder()
                // Header
                .setHeaderParam("alg", "HS256")
                .setHeaderParam("typ", "JWT")
                // Payload
                .setClaims(claims)
                // Signature
                .setExpiration(date)
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
        log.debug("生成的JWT：{}", jwt);
        return jwt;
    }
    @Override//手机号码登陆
    public String loginByPhone(UserLoginByPhoneDTO userLoginByPhoneDTO) {
        String phone=userLoginByPhoneDTO.getPhone()+"YZM";

        UserLoginInfoVO user=userMapper.getByPhone(userLoginByPhoneDTO.getPhone());
        if (user==null){
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND,"手机号不存在");
        }

        String redisCode=redisTemplate.opsForValue().get(phone);
        if (redisCode==null){
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND,"请发送验证码");
        }

        if (!redisCode.equalsIgnoreCase(userLoginByPhoneDTO.getCode())){
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND,"验证码错误");
        }
        //获取用户名
        String username = user.getUsername();
        //获取ID
        Long id = user.getId();
        //获取权限
        List<GrantedAuthority> auth = new ArrayList<>();
        for (String permission : user.getPermissions()) {
            GrantedAuthority authority = new SimpleGrantedAuthority(permission);
            auth.add(authority);
        }
        Collection<GrantedAuthority> authorities = auth;
        String authoritiesJsonString = JSON.toJSONString(authorities);
        // 生成JWT数据时，需要填充装到JWT中的数据
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", id);
        claims.put("username", username);
        claims.put("authoritiesJsonString", authoritiesJsonString);
        log.debug("向JWT中存入id：{}", id);
        log.debug("向JWT中存入username：{}", username);
        log.debug("向JWT中存入authoritiesJsonString：{}", authoritiesJsonString);
        // 以下是生成JWT的固定代码
        Date date = new Date(System.currentTimeMillis() + durationInMinute * 60 * 1000L);
        String jwt = Jwts.builder()
                // Header
                .setHeaderParam("alg", "HS256")
                .setHeaderParam("typ", "JWT")
                // Payload
                .setClaims(claims)
                // Signature
                .setExpiration(date)
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
        log.debug("生成的JWT：{}", jwt);
        redisTemplate.delete(phone);
        return jwt;


    }
    @Override//邮箱登陆
    public String loginByEmail(UserLoginByEmailDTO userLoginByEmailDTO) {

        UserLoginInfoVO user=userMapper.getByEmail(userLoginByEmailDTO.getEmail());
        if (user==null){
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND,"邮箱不存在");
        }

        String redisCode=redisTemplate.opsForValue().get(userLoginByEmailDTO.getEmail());
        if (redisCode==null){
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND,"请发送验证码");
        }

        if (!redisCode.equalsIgnoreCase(userLoginByEmailDTO.getCode())){
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND,"验证码错误");
        }
        //获取用户名
        String username = user.getUsername();
        //获取ID
        Long id = user.getId();
        //获取权限
        List<GrantedAuthority> auth = new ArrayList<>();
        for (String permission : user.getPermissions()) {
            GrantedAuthority authority = new SimpleGrantedAuthority(permission);
            auth.add(authority);
        }
        Collection<GrantedAuthority> authorities = auth;
        String authoritiesJsonString = JSON.toJSONString(authorities);
        // 生成JWT数据时，需要填充装到JWT中的数据
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", id);
        claims.put("username", username);
        claims.put("authoritiesJsonString", authoritiesJsonString);
        log.debug("向JWT中存入id：{}", id);
        log.debug("向JWT中存入username：{}", username);
        log.debug("向JWT中存入authoritiesJsonString：{}", authoritiesJsonString);
        // 以下是生成JWT的固定代码
        Date date = new Date(System.currentTimeMillis() + durationInMinute * 60 * 1000L);
        String jwt = Jwts.builder()
                // Header
                .setHeaderParam("alg", "HS256")
                .setHeaderParam("typ", "JWT")
                // Payload
                .setClaims(claims)
                // Signature
                .setExpiration(date)
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
        log.debug("生成的JWT：{}", jwt);
        redisTemplate.delete(userLoginByEmailDTO.getEmail());
        return jwt;

    }

    @Override//发送邮箱
    public void sendEmail(String email) {

        String redisCode=redisTemplate.opsForValue().get(email);
        System.out.println("redisCode = " + redisCode);
        if(redisCode!=null){
            throw new ServiceException(ServiceCode.ERR_CONFLICT,"发送失败");
        }

        UserLoginInfoVO user=userMapper.getByEmail(email);
        if (user==null){
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND,"邮箱不存在");
        }

        YzmUtil yzm=new YzmUtil();
        String to = email; // 这是个收件人邮箱，写成需要发送的邮箱地址就可以
        String title = "验证码";
        String YZM= yzm.Yzm();
        String content = YZM;
        emailUtil.sendMessage(to, title, content);
        //将验证码加入缓冲
        redisTemplate.opsForValue().set(email, YZM,1, TimeUnit.MINUTES);
    }

    @Override
    public void senPhone(Long phone) {
        String phone1=phone+"YZM";

        String redisCode=redisTemplate.opsForValue().get(phone1);
        System.out.println("redisCode = " + redisCode);
        if(redisCode!=null){
            throw new ServiceException(ServiceCode.ERR_CONFLICT,"发送失败");
        }

        UserLoginInfoVO user = userMapper.getByPhone(phone);
        if (user==null){
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND,"手机号不存在");
        }

        YzmUtil yzm=new YzmUtil();
        String yzm1= yzm.Yzm();

        //将验证码加入缓冲
        redisTemplate.opsForValue().set(phone1, yzm1,1, TimeUnit.MINUTES);
    }

    @Override
    public UserLoginByUserNameAndRoleVO selectByPhone(Long phone) {
        return  userMapper.selectByPhone(phone);
    }

    @Override
    public UserLoginByUserNameAndRoleVO selectByEmail(String email) {
        return userMapper.selectByEmail(email);
    }

    @Override
    public UserLoginByUserNameAndRoleVO selectByUserName(String username) {
        return userMapper.selectByUserName(username);
    }

    @Override
    public List<LocationListItemVO> selectByRegion() {
        return locationMapper.list();
    }
}
